// Copyright 2009 Pigasus.
// GNU GPL v2.
// Author: John Zhang and Jordan Rupprecht.

#include "formatstring.h"

#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <string>

#include "pigasus-types.h"

using namespace std;

#define BUFFER_SIZE 1000

// Given a format string fmt that is known to only contain one format specifier
// as well as the argument that is to be put into the string and the associated
// type, PrintChunk returns the formatted string (i.e. result of sprintf()).
// However, if fmt contained %%, then arg is actually not used, so we set
// arg_used to 0 and return. Otherwise, it is set to 1.
string PrintChunk(string fmt, void *arg, string type, int *arg_used) {
  char buf[BUFFER_SIZE];
  
  if (type == kIntType) {
    sprintf(buf, fmt.c_str(), *((int*)arg));
  } else if (type == kFloatType) {
    sprintf(buf, fmt.c_str(), *((double*)arg));
  } else if (type == kStringType) {
    sprintf(buf, fmt.c_str(), ((string*)arg)->c_str());
  } else {
    sprintf(buf, fmt.c_str()); // No more args.
  }

  string res(buf);
  if (res.find("%") != string::npos) {
    // That means fmt contained "%%" and arg was not used.
    *arg_used = 0;
  } else {
    *arg_used = 1;
  }
  return res;
}

#undef BUFFER_SIZE

// The FormatString function takes a printf() format string and a Pigasus List
// which should only contain ints, Floats, and string's, and returns the
// formatted string. This function works as follows. The general idea is to
// break the format string up into sections so that teach section contains only
// one format specifier (i.e. %something) and apply the next element of the List
// to that format string, and concatenate that to a result string, which is
// returned at the end. 
string FormatString(string fmt, const List &arg) {
  size_t found;
  string res;
  int prev_s = 0;
  int prev_m = 0;
  int used;
  int i = 0;  // Counter for args.

  List *args = (List*)(&arg);
  
  while (true) {
    // Look for the next occurrence of %.
    found = fmt.find("%", prev_s);
    if (found == string::npos) {
      // No other %'s found, so the rest of the format string can be taken as
      // is.
      res += fmt;
      break;
    } else {
      prev_m = int(found);
      
      // One occurrence of % has been found, now find the one after that.
      found = fmt.find("%", prev_m + 1);
      if (int(found) == prev_m + 1) {
        // We found a %%, so the second % doesn't count.
        found = fmt.find("%", prev_m + 2);
      }

      if (found == string::npos) {        
        // Only one % left in the remaining string. So take that chunk.
        res += PrintChunk(fmt.substr(prev_s, fmt.size() - prev_s + 1),
                          args->Get(i), args->GetType(i), &used);
        if (used) {
          i++;                 
        }
        break;
      } else {
        // Take the chunk containing the % at prev_m, which is located between
        // indices prev_s and int(found) and use that.
        res += PrintChunk(fmt.substr(prev_s, int(found) - prev_s),
                          args->Get(i), args->GetType(i), &used);
        if (used) {
          i++;
        }
        prev_s = int(found);
      }
    }
  }

  return res;
}

